#ifndef StartStop_h
#define StartStop_h

#include <sys/types.h>
#include "sis3100_vme_calls.h"

#include "VmeModule.h"
#include "IOError.h"

/**
	
IMPORTANT: The A201S, 6U VMEbus, Carrier Board only supports D16 access!

 **/


class StartStop : public VmeModule
{
 public:
		static const u_int16_t COUNTER1_OFFSET     = 0x0000;
		static const u_int16_t COUNTER2_OFFSET     = 0x0004;
		static const u_int16_t COUNTER3_OFFSET     = 0x0008;
		static const u_int16_t TIMERCOUNTER_OFFSET = 0x000C;
		static const u_int16_t PRESET1_OFFSET      = 0x0010;
		static const u_int16_t PRESET2_OFFSET      = 0x0014;
		static const u_int16_t PRESET3_OFFSET      = 0x0018;
		static const u_int16_t TIMERPRESET_OFFSET  = 0x001C;
		static const u_int16_t CSR_OFFSET          = 0x0020;
		static const u_int16_t STOP_OFFSET         = 0x0030;
		static const u_int16_t PAUSE_OFFSET        = 0x0034;
		static const u_int16_t START_OFFSET        = 0x0038;
		static const u_int16_t RESET_OFFSET        = 0x003C;
		static const u_int16_t CARDID_OFFSET       = 0x003C;

		static const u_int16_t TB_100ns            = 0x0000; // 0000 0000 0000 0000
		static const u_int16_t TB_1us              = 0x0001; // 0000 0000 0000 0001
		static const u_int16_t TB_10us             = 0x0002; // 0000 0000 0000 0010
		static const u_int16_t TB_100us            = 0x0003; // 0000 0000 0000 0011
		static const u_int16_t TB_1ms              = 0x0004; // 0000 0000 0000 0100
		static const u_int16_t TB_10ms             = 0x0005; // 0000 0000 0000 0101
		static const u_int16_t TB_100ms            = 0x0006; // 0000 0000 0000 0110

		static const u_int16_t TIMERPRESET_ENABLED = 0x0008; // 0000 0000 0000 1000
		static const u_int16_t COUNTER1_ENABLED    = 0x0010; // 0000 0000 0001 0000
		static const u_int16_t COUNTER2_ENABLED    = 0x0020; // 0000 0000 0010 0000
		static const u_int16_t COUNTER3_ENABLED    = 0x0040; // 0000 0000 0100 0000

		static const u_int16_t COUNT_INHIBITED     = 0x0400; // 0000 0100 0000 0000
		static const u_int16_t NO_PRESET           = 0x0800; // 0000 1000 0000 0000
		static const u_int16_t COUNT_TRIGGERED     = 0x1000; // 0001 0000 0000 0000
		static const u_int16_t COUNTING_ACTIVE     = 0x2000; // 0000010 0000 0000 0000
		static const u_int16_t COUNTING_FINISHED   = 0x4000; // 0100 0000 0000 0000
		static const u_int16_t COUNTING_PAUSED     = 0x8000; // 1000 0000 0000 0000

		StartStop( int handle, unsigned int address )
				: VmeModule(handle,address) {}

		void stop() throw(IOError)
		{
				int r;
						
				// to do a reset you may write anything to STOP_OFFSET
				if( (r=vme_A16D16_write(handle,address+STOP_OFFSET,0x1)) )
						throw IOError("StartStop::stop():\n"
													"  vme_A16D16_write() returned non zero",r);
		}

		// ??
		void togglePauseMode() throw(IOError)
		{
				int r;
						
				// to do a reset you may write anything to PAUSE_OFFSET
				if( (r=vme_A16D16_write(handle,address+PAUSE_OFFSET,0x1)) )
						throw IOError("StartStop::togglePauseMode():\n"
													"  vme_A16D16_write() returned non zero",r);
		}

		void start() throw(IOError)
		{
				int r;
						
				// to do a reset you may write anything to START_OFFSET
				if( (r=vme_A16D16_write(handle,address+START_OFFSET,0x1)) )
						throw IOError("StartStop::start():\n"
													"  vme_A16D16_write() returned non zero",r);
		}

		void reset() throw(IOError)
		{
				int r;
						
				// to do a reset you may write anything to RESET_OFFSET
				if( (r=vme_A16D16_write(handle,address+RESET_OFFSET,1)) )
						throw IOError("StartStop::reset():\n"
													"  vme_A16D16_write() returned non zero",r);
		} 

		u_int32_t getCardID() throw(IOError)
		{
				return A16D16drD32(CARDID_OFFSET);
		}

		u_int32_t getCounter1() throw(IOError)
		{
				return A16D16drD32(COUNTER1_OFFSET);
		}

		u_int32_t getCounter2() throw(IOError)
		{
				return A16D16drD32(COUNTER2_OFFSET);
		}

		u_int32_t getCounter3() throw(IOError)
		{
				return A16D16drD32(COUNTER3_OFFSET);
		}

		u_int32_t getTimerCounter() throw(IOError)
		{
				return A16D16drD32(TIMERCOUNTER_OFFSET);
		}

		void setPreset1(u_int32_t value) throw(IOError)
		{
				A16D16dwD32( PRESET1_OFFSET, value );
		} 

		void setPreset2(u_int32_t value) throw(IOError)
		{
				A16D16dwD32( PRESET2_OFFSET, value );
		} 

		void setPreset3(u_int32_t value) throw(IOError)
		{
				A16D16dwD32( PRESET3_OFFSET, value );
		} 

		void setTimerPreset(u_int32_t value) throw(IOError)
		{
				A16D16dwD32( TIMERPRESET_OFFSET, value );
		} 

		u_int32_t getPreset1() throw(IOError)
		{
				return A16D16drD32(PRESET1_OFFSET);
		}

		u_int32_t getPreset2() throw(IOError)
		{
				return A16D16drD32(PRESET2_OFFSET);
		}

		u_int32_t getPreset3() throw(IOError)
		{
				return A16D16drD32(PRESET3_OFFSET);
		}

		u_int32_t getTimerPreset() throw(IOError)
		{
				return A16D16drD32(TIMERPRESET_OFFSET);
		}

		u_int16_t getControlRegister() throw(IOError)
		{
				int r;
				u_int16_t d;

				if( (r=vme_A16D16_read(handle,address+CSR_OFFSET,&d)) )
						throw IOError("StartStop::getControlRegister():\n"
													"  vme_A16D16_read() returned non zero", r);
				return d;
		}

		void setControlRegister(u_int16_t pattern) throw(IOError)
		{
				int r;
						
				if( (r=vme_A16D16_write( handle, address+CSR_OFFSET,
																 pattern ) ) )
						throw IOError("StartStop::setControlRegister:\n"
													"  vme_A16D16_write() returned non zero",r);
		}

 private:
		void A16D16dwD32(u_int16_t offset,u_int32_t value )
		{
				int r;
						
				// write low word
				if( (r=vme_A16D16_write( handle, address+offset+2,
																 (u_int16_t)(value & 0xffff) ) ) )
						throw IOError("StartStop::setPreset1(), writiong low word:\n"
													"  vme_A16D16_write() returned non zero",r);
				// write high word
				if( (r=vme_A16D16_write( handle, address+offset,
																 (u_int16_t)(value >> 16) ) ) )
						throw IOError("StartStop::setPreset1(), writiong high word:\n"
													"  vme_A16D16_write() returned non zero",r);
		}

		u_int32_t A16D16drD32(u_int16_t offset)
		{
				int r;
				u_int16_t hi,lo;

				if( (r=vme_A16D16_read(handle,address+offset+2,&lo)) )
						throw IOError("StartStop::A16D16drD32(), reading low word:\n"
													"  vme_A16D16_read() returned non zero",r);
				if( (r=vme_A16D16_read(handle,address+offset,&hi)) )
						throw IOError("StartStop::A16D16drD32(), reading high word:\n"
													"  vme_A16D16_read() returned non zero",r);

				return ((((u_int32_t)(hi)) << 16) | lo);
		}
		
};

#endif
